package com.yunhe.abnormal.repository.base.impl;

import com.google.common.collect.Lists;
import com.yunhe.common.constant.DeviceConsts;
import com.yunhe.abnormal.domain.base.AlarmCalculation;
import com.yunhe.abnormal.domain.base.AlarmRuleInstance;
import com.yunhe.abnormal.repository.base.AlarmRuleInstanceSession;
import org.neo4j.ogm.model.Result;
import org.neo4j.ogm.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.*;

/**
 * 异常规则实例Session实现类
 * @author liuronglei
 */
@Repository
public class AlarmRuleInstanceSessionImpl implements AlarmRuleInstanceSession {
    @Autowired
    private Session session;

    /**
     * 获得返回语句
     */
    private String getReturnCypher() {
        StringBuilder query = new StringBuilder();
        query.append(" return ari")
                .append(",(ari)-[:ALARM_RULE_OF]->(:AlarmRule)-[]->()")
                .append(",(ari)-[:STATION_OF]->(:Station)")
                .append(",(ari)-[:DEVICE_OF]->(:Device)");
        return query.toString();
    }

    /**
     * 从结果中获取计算实例对象列表
     * @param results 执行结果
     */
    private List<AlarmCalculation> getCalculationList(Result results) {
        List<AlarmCalculation> list = new ArrayList<>();
        for (Map<String, Object> result : results) {
            Map<String, Object> object = (Map<String, Object>) result.get("ari");
            if (object != null) {
                AlarmCalculation calculation = new AlarmCalculation();
                Optional.ofNullable(object.get("id")).ifPresent(id -> calculation.setId((Long)id));
                Optional.ofNullable(object.get("points")).ifPresent(points -> calculation.setPoints((List<Long>)points));
                Optional.ofNullable(object.get("relatedPoint")).ifPresent(relatedPoint -> calculation.setRelatedPoints((List<Long>)relatedPoint));
                Optional.ofNullable(object.get("formula")).ifPresent(formula -> calculation.setFormula((String)formula));
                Optional.ofNullable(object.get("stationId")).ifPresent(stationId -> calculation.setStationId((Long)stationId));
                Optional.ofNullable(object.get("levelId")).ifPresent(levelId -> calculation.setLevelId((Long)levelId));
                Optional.ofNullable(object.get("levelName")).ifPresent(levelName -> calculation.setLevelName((String)levelName));
                Optional.ofNullable(object.get("levelTitle")).ifPresent(levelTitle -> calculation.setLevelTitle((String)levelTitle));
                Optional.ofNullable(object.get("desc")).ifPresent(desc -> calculation.setDesc((String)desc));
                Optional.ofNullable(object.get("typeId")).ifPresent(typeId -> calculation.setTypeId((Long)typeId));
                Optional.ofNullable(object.get("typeTitle")).ifPresent(typeTitle -> calculation.setTypeTitle((String)typeTitle));
                if (DeviceConsts.DEVICE_TYPE.Station.equals(object.get("relLabel"))) {
                    Optional.ofNullable(object.get("relId")).ifPresent(relId -> calculation.setRelStationId((Long)relId));
                    Optional.ofNullable(object.get("relTitle")).ifPresent(relTitle -> calculation.setRelStationTitle((String)relTitle));
                } else {
                    Optional.ofNullable(object.get("relId")).ifPresent(relId -> calculation.setRelDeviceId((Long)relId));
                    Optional.ofNullable(object.get("relTitle")).ifPresent(relTitle -> calculation.setRelDeviceTitle((String)relTitle));
                }
                list.add(calculation);
            }
        }
        return list;
    }

    @Override
    public void deleteByRuleId(Long alarmRuleId) {
        String query = "match(ari:AlarmRuleInstance) where ari.relatedAlarmRuleId={alarmRuleId} detach delete ari";
        Map<String, Object> params = new HashMap<>();
        params.put("alarmRuleId", alarmRuleId);
        session.query(query, params);
    }

    @Override
    public void deleteByStationId(Long stationId) {
        String query = "match(ari:AlarmRuleInstance) where ari.relatedStationId={stationId} detach delete ari";
        Map<String, Object> params = new HashMap<>();
        params.put("stationId", stationId);
        session.query(query, params);
    }

    @Override
    public void deleteByDeviceId(Long deviceId) {
        String query = "match(ari:AlarmRuleInstance) where ari.relatedDeviceId={deviceId} detach delete ari";
        Map<String, Object> params = new HashMap<>();
        params.put("deviceId", deviceId);
        session.query(query, params);
    }

    @Override
    public void unBindRelat(Long alarmRuleInstanceId) {
        String query = "match(ari:AlarmRuleInstance)-[r]->(n) where id(ari)={alarmRuleInstanceId} delete r";
        Map<String, Object> params = new HashMap<>();
        params.put("alarmRuleInstanceId", alarmRuleInstanceId);
        session.query(query, params);
    }

    @Override
    public void bindRelat(AlarmRuleInstance alarmRuleInstance) {
        List<String> match = new ArrayList<>();
        List<String> condition = new ArrayList<>();
        List<String> create = new ArrayList<>();
        Map<String, Object> params = new HashMap<>();
        params.put("alarmRuleInstanceId", alarmRuleInstance.getId());
        if (alarmRuleInstance.getAlarmRule() != null) {
            match.add("(ar:AlarmRule)");
            condition.add("id(ar)={alarmRuleId}");
            create.add("(ari)-[:ALARM_RULE_OF]->(ar)");
            params.put("alarmRuleId", alarmRuleInstance.getAlarmRule().getId());
        }
        if (alarmRuleInstance.getStation() != null) {
            match.add("(s:Station)");
            condition.add("id(s)={stationId}");
            create.add("(ari)-[:STATION_OF]->(s)");
            params.put("stationId", alarmRuleInstance.getStation().getId());
        }
        if (alarmRuleInstance.getDevice() != null) {
            match.add("(d:Device)");
            condition.add("id(d)={deviceId}");
            create.add("(ari)-[:DEVICE_OF]->(d)");
            params.put("deviceId", alarmRuleInstance.getDevice().getId());
        }
        for (int i = 0; i < match.size(); i++) {
            String query = "match(ari:AlarmRuleInstance)," + match.get(i) + " where id(ari)={alarmRuleInstanceId} and " + condition.get(i) +
                    " create " + create.get(i);
            session.query(query, params);
        }
    }

    @Override
    public List<AlarmRuleInstance> getByRuleId(Long alarmRuleId) {
        String query = "match(ari:AlarmRuleInstance) where ari.relatedAlarmRuleId={alarmRuleId}" + getReturnCypher();
        Map<String, Object> params = new HashMap<>();
        params.put("alarmRuleId", alarmRuleId);
        return Lists.newArrayList(session.query(AlarmRuleInstance.class, query, params));
    }

    @Override
    public List<AlarmRuleInstance> getByStationId(Long stationId) {
        String query = "match(ari:AlarmRuleInstance) where ari.stationId={stationId}" + getReturnCypher();
        Map<String, Object> params = new HashMap<>();
        params.put("stationId", stationId);
        return Lists.newArrayList(session.query(AlarmRuleInstance.class, query, params));
    }

    @Override
    public List<AlarmRuleInstance> getStationBind(Long stationId) {
        StringBuilder query = new StringBuilder();
        query.append("match(ari:AlarmRuleInstance)-[:STATION_OF]->(s:Station) where id(s)={stationId}")
                .append(getReturnCypher());
        Map<String, Object> params = new HashMap<>();
        params.put("stationId", stationId);
        return Lists.newArrayList(session.query(AlarmRuleInstance.class, query.toString(), params));
    }

    @Override
    public List<AlarmRuleInstance> getDeviceBind(Long deviceId) {
        StringBuilder query = new StringBuilder();
        query.append("match(ari:AlarmRuleInstance)-[:DEVICE_OF]->(d:Device) where id(d)={deviceId}")
                .append(getReturnCypher());
        Map<String, Object> params = new HashMap<>();
        params.put("deviceId", deviceId);
        return Lists.newArrayList(session.query(AlarmRuleInstance.class, query.toString(), params));
    }

    @Override
    public List<AlarmCalculation> getAlarmCalculation(Long stationId, Long stationCode) {
        HashMap<String, Object> params = new HashMap<>();
        StringBuilder query = new StringBuilder("match(ari:AlarmRuleInstance),(ari)-[:STATION_OF|:DEVICE_OF]->(obj)");
        query.append(",(ari)-[:ALARM_RULE_OF]->(ar:AlarmRule),(ar)-[:ALARM_TYPE_OF]->(at:AlarmType),(ar)-[:ALARM_LEVEL_OF]->(al:AlarmLevel)");
        if (stationId != null) {
            query.append(" where ari.stationId={stationId}");
            params.put("stationId", stationId);
        } else if (stationCode != null) {
            query.append(",(s:Station) where s.code={stationCode} and ari.stationId=id(s)");
            params.put("stationCode", stationCode);
        } else {
            return new ArrayList<>();
        }
        query.append(" return ari{id:id(ari),.*,desc:ar.desc,levelId:id(al),levelName:al.name,levelTitle:al.title")
                .append(",typeId:id(at),typeTitle:at.title,relLabel:labels(obj)[0],relId:id(obj),relTitle:obj.title}");
        Result result = session.query(query.toString(), params);
        return getCalculationList(result);
    }

    @Override
    public List<AlarmCalculation> getAlarmCalculationByInstanceIds(List<Long> instanceIds) {
        Map<String, Object> params = new HashMap<>(1);
        params.put("instanceIds", instanceIds);
        String query = "match(ari:AlarmRuleInstance) where ID(ari) in {instanceIds} with ari match(ari)-[:STATION_OF|:DEVICE_OF]->(obj)," +
                "(ari)-[:ALARM_RULE_OF]->(ar:AlarmRule),(ar)-[:ALARM_TYPE_OF]->(at:AlarmType),(ar)-[:ALARM_LEVEL_OF]->(al:AlarmLevel)" +
                " return ari{id:id(ari),.*,desc:ar.desc,levelId:id(al),levelName:al.name,levelTitle:al.title" +
                ",typeId:id(at),typeTitle:at.title,relLabel:labels(obj)[0],relId:id(obj),relTitle:obj.title}";
        Result result = session.query(query, params);
        return getCalculationList(result);
    }
}
